package com.ld.shieldsb.dao;

import java.sql.Connection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.util.StackTraceUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 数据源链接相关
 * 
 * @ClassName DataSourceFactory
 * @author <a href="mailto:donggongai@126.com" target="_blank">kevin</a>
 * @date 2016年8月25日 下午2:23:37
 *
 */
@Slf4j
public class DataSourceFactory {

    private static Map<String, DataSource> DATA_SOURCE_MAP = new HashMap<String, DataSource>();
    private static Map<String, ThreadLocal<Connection>> CONNECTION_THREAD_LOCAL_MAP = new HashMap<String, ThreadLocal<Connection>>();
    private static Map<String, ThreadLocal<Exception>> EXCEPTION_THREAD_LOCAL_MAP = new HashMap<String, ThreadLocal<Exception>>();
    // 以下用于非配置文件传入数据库连接，动态传入
    private static Map<String, DataSource> DATA_SOURCE_EXTEND_MAP = new HashMap<String, DataSource>();
    private static Map<String, ThreadLocal<Connection>> CONNECTION_THREAD_LOCAL_EXTEND_MAP = new HashMap<String, ThreadLocal<Connection>>();
    private static Map<String, ThreadLocal<Exception>> EXCEPTION_THREAD_LOCAL_EXTEND_MAP = new HashMap<String, ThreadLocal<Exception>>();

    /**
     * 
     * 获取数据源
     * 
     * @Title getDataSource
     * @param key
     *            config配置的数据源关键词
     * @return DataSource
     */
    public static DataSource getDataSource(String key) {
        DataSource dataSource = DATA_SOURCE_MAP.get(key);
        if (dataSource == null) {
            synchronized (DATA_SOURCE_MAP) {
                dataSource = DATA_SOURCE_MAP.get(key);
                if (dataSource == null) {
                    dataSource = createDataSource(key);
                    DATA_SOURCE_MAP.put(key, dataSource);
                }
            }
        }
        return dataSource;
    }

    /**
     * 
     * 获取连接线程
     * 
     * @Title getThreadLocalConnection
     * @param key
     *            config配置的数据源关键词
     * @return ThreadLocal<Connection>
     */
    public static ThreadLocal<Connection> getThreadLocalConnection(String key) {
        ThreadLocal<Connection> connectionThreadLocal = CONNECTION_THREAD_LOCAL_MAP.get(key);
        if (connectionThreadLocal == null) {
            synchronized (CONNECTION_THREAD_LOCAL_MAP) {
                connectionThreadLocal = CONNECTION_THREAD_LOCAL_MAP.get(key);
                if (connectionThreadLocal == null) {
                    connectionThreadLocal = new ThreadLocal<Connection>();
                    CONNECTION_THREAD_LOCAL_MAP.put(key, connectionThreadLocal);
                }
            }
        }
        return connectionThreadLocal;
    }

    /**
     * 
     * 获取异常线程
     * 
     * @Title getThreadLocalException
     * @param key
     *            config配置的数据源关键词
     * @return ThreadLocal<Exception>
     */
    public static ThreadLocal<Exception> getThreadLocalException(String key) {
        ThreadLocal<Exception> exceptionThreadLocal = EXCEPTION_THREAD_LOCAL_MAP.get(key);
        if (exceptionThreadLocal == null) {
            synchronized (EXCEPTION_THREAD_LOCAL_MAP) {
                exceptionThreadLocal = EXCEPTION_THREAD_LOCAL_MAP.get(key);
                if (exceptionThreadLocal == null) {
                    exceptionThreadLocal = new ThreadLocal<Exception>();
                    EXCEPTION_THREAD_LOCAL_MAP.put(key, exceptionThreadLocal);
                }
            }
        }
        return exceptionThreadLocal;
    }

    public static void main(String[] args) {
        System.out.println(PropertiesModel.CONFIG.getString("db0_connectionProperties"));
    }

    /**
     * 
     * 加载数据库
     * 
     * @Title createDataSource
     * @param prefix
     * @return DataSource
     */
    public static DataSource createDataSource(String prefix) {
        Properties properties = PropertiesModel.CONFIG.getProperties(prefix);
        boolean initProject = PropertiesModel.CONFIG.getBoolean("initProject", true);
        if (!initProject) {
            log.warn("未初始化项目！");
            return null;
        } else {
            String outStr = "加载数据库，url:" + properties.getProperty("url") + "，prefix:" + prefix + ",username:"
                    + properties.getProperty("username");
            if (log.isWarnEnabled()) {
                log.warn(outStr);
            } else {
                System.out.println("[" + new Date() + "]" + outStr + "   " + StackTraceUtil.getLastTraceEle());
            }
            try {
                return DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                log.error("数据库加载失败", e);
                System.exit(-1);
                return null;
            }
        }
    }

    /**
     * 传入具体的url，userName，password进行处理
     * 
     * @Title createDataSource
     * @author 吕凯
     * @date 2018年2月3日 上午9:20:09
     * @param url
     * @param userName
     * @param password
     * @return DataSource
     */
    public static DataSource createDataSource(String url, String userName, String password) {
        Map<String, String> sourceMap = new HashMap<>();
        sourceMap.put("url", url);
        sourceMap.put("username", userName);
        sourceMap.put("password", password);
        log.warn("加载数据库，url:" + url + ",username:" + userName);
        try {
            return DruidDataSourceFactory.createDataSource(sourceMap);
        } catch (Exception e) {
            log.error("数据库加载失败", e);
            System.exit(-1);
            return null;
        }
    }

    /**
     * 传入 key 和 dataSource 扔到 DATA_SOURCE_MAP 中
     *
     * @author hansai
     * @date 2019-04-16
     */
    public static void putDataSource(String key, DataSource dataSource) {
        DATA_SOURCE_MAP.put(key, dataSource);
        log.warn("外部注入 DataSource,key: {}，dataSource: {}", key, dataSource);
    }

    /**
     * 
     * 获取数据源
     * 
     * @Title getDataSource
     * @param key
     *            config配置的数据源关键词
     * @return DataSource
     */
    public static DataSource getDataSource(String url, String userName, String password) {
        String key = url + userName;
        DataSource dataSource = DATA_SOURCE_EXTEND_MAP.get(key);
        if (dataSource == null) {
            synchronized (DATA_SOURCE_EXTEND_MAP) {
                dataSource = DATA_SOURCE_EXTEND_MAP.get(key);
                if (dataSource == null) {
                    dataSource = createDataSource(url, userName, password);
                    DATA_SOURCE_EXTEND_MAP.put(key, dataSource);
                }
            }
        }
        return dataSource;
    }

    /**
     * 
     * 获取连接线程
     * 
     * @Title getThreadLocalConnection
     * @param key
     *            config配置的数据源关键词
     * @return ThreadLocal<Connection>
     */
    public static ThreadLocal<Connection> getThreadLocalConnection(String url, String userName) {
        String key = url + userName;
        ThreadLocal<Connection> connectionThreadLocal = CONNECTION_THREAD_LOCAL_EXTEND_MAP.get(key);
        if (connectionThreadLocal == null) {
            synchronized (CONNECTION_THREAD_LOCAL_EXTEND_MAP) {
                connectionThreadLocal = CONNECTION_THREAD_LOCAL_EXTEND_MAP.get(key);
                if (connectionThreadLocal == null) {
                    connectionThreadLocal = new ThreadLocal<Connection>();
                    CONNECTION_THREAD_LOCAL_EXTEND_MAP.put(key, connectionThreadLocal);
                }
            }
        }
        return connectionThreadLocal;
    }

    /**
     * 
     * 获取异常线程
     * 
     * @Title getThreadLocalException
     * @param key
     *            config配置的数据源关键词
     * @return ThreadLocal<Exception>
     */
    public static ThreadLocal<Exception> getThreadLocalException(String url, String userName) {
        String key = url + userName;
        ThreadLocal<Exception> exceptionThreadLocal = EXCEPTION_THREAD_LOCAL_EXTEND_MAP.get(key);
        if (exceptionThreadLocal == null) {
            synchronized (EXCEPTION_THREAD_LOCAL_EXTEND_MAP) {
                exceptionThreadLocal = EXCEPTION_THREAD_LOCAL_EXTEND_MAP.get(key);
                if (exceptionThreadLocal == null) {
                    exceptionThreadLocal = new ThreadLocal<Exception>();
                    EXCEPTION_THREAD_LOCAL_EXTEND_MAP.put(key, exceptionThreadLocal);
                }
            }
        }
        return exceptionThreadLocal;
    }

    /**
     * 传入具体的url，userName，password进行处理
     * 
     * @Title createDataSource
     * @author 吕凯
     * @date 2018年2月3日 上午9:20:09
     * @param url
     * @param userName
     * @param password
     * @return DataSource
     */
    /**
     * 
     * 获取数据源
     * 
     * @Title getDataSource
     * @param key
     *            config配置的数据源关键词
     * @return DataSource
     */
    public static DataSource getDataSource(Map<String, String> sourceMap) {
        String url = sourceMap.get("url");
        String userName = sourceMap.get("username");
        String key = url + userName;
        DataSource dataSource = DATA_SOURCE_EXTEND_MAP.get(key);
        if (dataSource == null) {
            synchronized (DATA_SOURCE_EXTEND_MAP) {
                dataSource = DATA_SOURCE_EXTEND_MAP.get(key);
                if (dataSource == null) {
                    dataSource = createDataSource(sourceMap);
                    DATA_SOURCE_EXTEND_MAP.put(key, dataSource);
                }
            }
        }
        return dataSource;
    }

    /**
     * 出入map，创建datasource
     * 
     * @Title createDataSource
     * @author 吕凯
     * @date 2018年2月3日 上午9:21:22
     * @param sourceMap
     * @return DataSource
     */
    public static DataSource createDataSource(Map<String, String> sourceMap) {
        log.warn("加载数据库，url:" + sourceMap.get("url") + ",username:" + sourceMap.get("username"));
        try {
            return DruidDataSourceFactory.createDataSource(sourceMap);
        } catch (Exception e) {
            log.error("数据库加载失败", e);
            System.exit(-1);
            return null;
        }
    }

}
